import { isArray, throttle, debounce, isObject, isFunction } from "@pureadmin/utils";
import { useEventListener } from "@vueuse/core";
import type { Directive, DirectiveBinding } from "vue";

export interface OptimizeOptions {
    /** 事件名 */
    event: string;
    /** 事件触发的方法 */
    fn: (...params: any) => any;
    /** 是否立即执行 */
    immediate?: boolean;
    /** 防抖或节流的延迟时间（防抖默认：`200`毫秒、节流默认：`1000`毫秒） */
    timeout?: number;
    /** 传递的参数 */
    params?: any;
}

/** 防抖（v-optimize或v-optimize:debounce）、节流（v-optimize:throttle）指令 */
export const optimize: Directive = {
    mounted(el: HTMLElement, binding: DirectiveBinding<OptimizeOptions>) {
        const { value } = binding;
        const optimizeType = binding.arg ?? "debounce";
        const type = ["debounce", "throttle"].find(t => t === optimizeType);
        if (type) {
            if (value && value.event && isFunction(value.fn)) {
                let params = value?.params;
                if (params) {
                    if (isArray(params) || isObject(params)) {
                        params = isObject(params) ? Array.of(params) : params;
                    } else {
                        throw new Error("[Directive: optimize]: `params` must be an array or object");
                    }
                }
                // Register using addEventListener on mounted, and removeEventListener automatically on unmounted
                useEventListener(
                    el,
                    value.event,
                    type === "debounce"
                        ? debounce(
                              params ? () => value.fn(...params) : value.fn,
                              value?.timeout ?? 200,
                              value?.immediate ?? false
                          )
                        : throttle(params ? () => value.fn(...params) : value.fn, value?.timeout ?? 1000)
                );
            } else {
                throw new Error("[Directive: optimize]: `event` and `fn` are required, and `fn` must be a function");
            }
        } else {
            throw new Error("[Directive: optimize]: only `debounce` and `throttle` are supported");
        }
    }
};
